package org.jetlinks.pro.assets.aop;


import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.hswebframework.web.api.crud.entity.QueryParamEntity;
import org.hswebframework.web.authorization.exception.AccessDenyException;
import org.hswebframework.web.bean.FastBeanCopier;
import org.jetlinks.pro.assets.*;
import org.jetlinks.pro.assets.annotation.AssetsController;
import org.jetlinks.pro.assets.annotation.MultiAssetsController;
import org.jetlinks.pro.assets.impl.UndefinedAssetType;
import org.jetlinks.reactor.ql.utils.CastUtils;
import org.reactivestreams.Publisher;
import org.springframework.core.ResolvableType;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.transaction.reactive.TransactionalOperator;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 基于AOP来进行资产数据权限控制.根据注解{@link AssetsController}的描述来进行控制.
 * <p>
 * 处理逻辑:
 * <ul>
 *     <li>
 *         方法参数为QueryParamEntity时使用: {@link QueryParamHandler}
 *     </li>
 *     <li>
 *         方法参数为Mono&lt;QueryParamEntity&gt;时使用: {@link MonoQueryParamHandler}
 *     </li>
 *     <li>
 *         参数是不为Mono或者Flux的处理方式: {@link AssetsHandler}
 *     </li>
 *     <li>
 *         方法参数Flux的处理方式: {@link FluxAssetsHandler} ,{@link FluxAutoBindHandler}
 *     </li>
 *     <li>
 *         方法参数Mono的处理方式: {@link MonoAssetHandler} ,{@link MonoAutoBindHandler}
 *     </li>
 *     <li>
 *         注解{@link MultiAssetsController}时使用{@link CompositeHandler}
 *     </li>
 * </ul>
 *
 * @author zhouhao
 * @see MultiAssetsController
 * @see AssetsController
 * @see AssetsHolder
 * @see AssetBindManager
 * @since 1.10
 */
@Slf4j
@AllArgsConstructor
public class AopAssetsControllerHandler implements MethodInterceptor {

    //方法对应的处理器缓存
    private static final Map<CacheKey, Handler> handlers = new ConcurrentHashMap<>();
    //事务控制器
    private final TransactionalOperator transactionalOperator;
    //资产绑定管理器
    private final AssetBindManager assetBindManager;

    @Override
    @SneakyThrows
    @SuppressWarnings("all")
    public Object invoke(MethodInvocation methodInvocation) {

        //被调用的方法
        Method method = methodInvocation.getMethod();
        //获取原始类,因为Controller类可能会被代理
        Class<?> target = ClassUtils.getUserClass(methodInvocation.getThis());
        //获取根据方法处理器
        Handler handler = handlers.computeIfAbsent(new CacheKey(method, target), this::createHandler);
        //执行前调用处理器
        if (handler.getPhased() == Phased.before || handler.getPhased() == Phased.both) {
            handler.handle(methodInvocation.getArguments(), null, Phased.before);
        }
        //执行原始逻辑
        Object result = methodInvocation.proceed();
        //执行后调用处理器
        if (handler.getPhased() == Phased.after || handler.getPhased() == Phased.both) {
            result = handler.handle(methodInvocation.getArguments(), result, Phased.after);
        }
        //设置为自动绑定,通过aop拦截的操作都认为是自动绑定.
        //可以在监听资产事件是通过判断是否为自动绑定来进行一些处理.
        if (result instanceof Mono) {
            result = ((Mono<?>) result).as(AssetsUtils.setMonoBindSource(AssetBindManager.SOURCE_AUTO));
        } else if (result instanceof Flux) {
            result = ((Flux<?>) result).as(AssetsUtils.setFluxBindSource(AssetBindManager.SOURCE_AUTO));
        }
        //控制事务
        if (handler.transactional()) {
            if (result instanceof Mono) {
                return transactionalOperator.transactional(((Mono<?>) result));
            }
            if (result instanceof Flux) {
                return transactionalOperator.transactional(((Flux<?>) result));
            }
        }
        return result;
    }

    public Handler createHandler(CacheKey key) {
        Method method = key.method;
        Class<?> type = key.target;
        //方法上的注解
        AssetsController methodAnn = AnnotatedElementUtils.findMergedAnnotation(method, AssetsController.class);
        //类型上的注解
        AssetsController typeAnn = AnnotatedElementUtils.findMergedAnnotation(type, AssetsController.class);
        //同时控制多种资产
        MultiAssetsController multiAssets = AnnotatedElementUtils.findMergedAnnotation(method, MultiAssetsController.class);
        if (null != multiAssets) {
            CompositeHandler handler = new CompositeHandler();
            for (AssetsController tenantAssets : multiAssets.value()) {
                AssetsDefine define = AssetsDefine.of(typeAnn, tenantAssets);
                //必须指定type
                if (StringUtils.isEmpty(define.type)) {
                    throw new UnsupportedOperationException("没有指定资产类型:" + type);
                }
                handler.addHandler(createHandler(method, define));
            }
            return handler;
        } else {
            AssetsDefine define = AssetsDefine.of(typeAnn, methodAnn);
            //必须指定type
            if (StringUtils.isEmpty(define.type)) {
                throw new UnsupportedOperationException("没有指定资产类型:" + type);
            }
            //没有注解,则啥也不做
            if (methodAnn == null && typeAnn == null) {
                return Handler.DO_NOTING;
            }
            //创建handler
            return createHandler(method, define);
        }
    }

    private static class AssetsDefine {
        static final AssetPermission[] emptyPermission = new AssetPermission[0];
        //资产类型
        private String type;
        //资产ID参数索引
        private int assetIdIndex = -1;
        //资产对象参数索引
        private int assetObjectIndex = -1;
        //在资产对象里表示资产ID的字段
        private String property = "id";
        //必须要有资产持有信息,否则抛出未授权异常
        private boolean required = false;
        //是否自动绑定,通常在新增数据时
        private boolean autoBind = false;
        //是否自动解绑
        private boolean autoUnBind = false;
        //是否解绑全部
        private boolean unbindAll = true;
        //是否忽略查询
        private boolean ignoreQuery = false;
        //是否验证权限
        private boolean validate = true;
        //资产不存在时是否允许操作
        private boolean allowAssetNotExist = false;
        //需要验证的操作权限
        private AssetPermission[] permission = emptyPermission;
        //资产类型对象
        private AssetType assetType;

        private void apply(AssetsController ann) {
            //注解上指定了type
            if (StringUtils.hasText(ann.type())) {
                type = ann.type();
                assetType = AssetType.of(type);
                //提示没有定义的资产类型
                if (assetType instanceof UndefinedAssetType) {
                    log.warn("资产类型{},未明确定义或者未加载.", type);
                }
            }
            //代表资产ID的参数索引,从0开始
            if (ann.assetIdIndex() != -1) {
                assetIdIndex = ann.assetIdIndex();
            }
            //代表资产对象的参数索引,从0开始
            if (ann.assetObjectIndex() != -1) {
                assetObjectIndex = ann.assetObjectIndex();
            }
            //在资产对象中代表资产ID的字段
            if (!"id".equals(ann.property())) {
                property = ann.property();
            }

            //以下配置:默认为false,有任意一次设置为true,则为true.
            //忽略查询条件
            ignoreQuery = ignoreQuery || ann.ignoreQuery();
            //自动绑定
            autoBind = autoBind || ann.autoBind();
            //自动绑定
            autoUnBind = autoUnBind || ann.autoUnbind();
            //资产不存在时是否允许访问,通常用于新建数据时
            allowAssetNotExist = allowAssetNotExist || ann.allowAssetNotExist();
            //要求当前用户必须是资产相关持有者
            required = required || ann.required();

            //是否解绑全部,默认为true,任意一次有设置为false时,则为false
            if (!ann.unbindAll()) {
                unbindAll = false;
            }
            //是否进行权限验证,默认为true,任意一次有设置为false时,则为false
            if (!ann.validate()) {
                validate = false;
            }
            //指定了对数据的权限限制
            if (ann.permission().length > 0 && assetType != null) {
                permission = assetType
                    .getPermissions(Arrays.asList(ann.permission()))
                    .toArray(emptyPermission);
            }

        }

        private static AssetsDefine of(AssetsController typeAnn, AssetsController methodAnn) {
            AssetsDefine define = new AssetsDefine();

            //类上有注解,并且方法上没有注解或者注解设置类合并类的注解
            if (typeAnn != null && (methodAnn == null || methodAnn.merge())) {
                define.apply(typeAnn);
            }
            //方法上的注解
            if (methodAnn != null) {
                define.apply(methodAnn);
            }

            return define;
        }
    }

    private Mono<Void> doUnbind(AssetsDefine define, AssetsHolder holder, Collection<?> objects) {
        return define.unbindAll
            //解绑全部
            ? assetBindManager.unbindAllAssets(define.type, objects).then()
            //解绑当前资产持有者
            : holder.unbindAssets(define.type, objects);
    }

    public Handler createHandler(Method method, AssetsDefine ann) {

        int index = 0;
        //是否返回Mono
        boolean returnMono = method.getReturnType().isAssignableFrom(Mono.class);
        //是否返回Flux
        boolean returnFlux = method.getReturnType().isAssignableFrom(Flux.class);
        if (!returnMono && !returnFlux) {
            log.warn("方法不支持响应式:{}", method);
            return Handler.DO_NOTING;
        }
        //方法没有参数啥也不做
        if (method.getParameterCount() == 0) {
            return Handler.DO_NOTING;
        }

        //没有忽略动态查询参数,则尝试获取参数中的动态查询条件
        if (!ann.ignoreQuery) {
            for (Parameter parameter : method.getParameters()) {
                ResolvableType type = ResolvableType.forMethodParameter(method, index);
                Class<?> argType = type.toClass();

                //参数为QueryParamEntity
                if (QueryParamEntity.class.isAssignableFrom(argType)) {
                    return new QueryParamHandler(index, ann, returnMono, returnFlux);
                }
                //参数为 Mono<QueryParamEntity>
                if (Mono.class.isAssignableFrom(argType)) {
                    Class<?> gen = type.getGeneric(0).toClass();
                    if (QueryParamEntity.class.isAssignableFrom(gen)) {
                        return new MonoQueryParamHandler(index, ann);
                    }
                }
                index++;
            }
        }

        //按对象处理权限
        if (ann.assetObjectIndex != -1) {
            index = ann.assetObjectIndex;
            //参数类型
            ResolvableType type = ResolvableType.forMethodParameter(method, index);
            //参数为 Mono
            if (Mono.class.isAssignableFrom(type.toClass())) {
                //标记了自动绑定
                if (ann.autoBind) {
                    return new MonoAutoBindHandler(index, ann, returnMono, returnFlux,
                                                   obj -> getProperty(obj, ann.property),
                                                   (tenant, objects) -> tenant.bindAssets(ann.type, objects)
                    );
                }
                //标记了自动解绑
                else if (ann.autoUnBind) {
                    return new MonoAutoBindHandler(index, ann, returnMono, returnFlux,
                                                   obj -> getProperty(obj, ann.property),
                                                   (tenant, objects) -> doUnbind(ann, tenant, objects)
                    );
                }
                //默认处理
                return new MonoAssetHandler(index, ann, asset -> getProperty(asset, ann.property));
            }
            //参数为 Flux
            if (Flux.class.isAssignableFrom(type.toClass())) {
                //标记了自动绑定
                if (ann.autoBind) {
                    return new FluxAutoBindHandler(index, ann, returnMono, returnFlux,
                                                   list -> getProperties(list, ann.property),
                                                   (tenant, objects) -> tenant.bindAssets(ann.type, objects)
                    );
                }
                //标记了自动解绑
                else if (ann.autoUnBind) {

                    return new FluxAutoBindHandler(index, ann, returnMono, returnFlux,
                                                   list -> getProperties(list, ann.property),
                                                   (tenant, objects) -> doUnbind(ann, tenant, objects)
                    );
                }
                return new FluxAssetsHandler(index, ann, assets -> getProperties(assets, ann.property));
            }
            //参数为其他(非响应式的参数)
            //标记了自动绑定
            if (ann.autoBind) {
                return new AutoBindHandler(index, ann, returnMono, returnFlux,
                                           list -> getProperties(list, ann.property),
                                           (tenant, objects) -> tenant.bindAssets(ann.type, objects)
                );
            }
            //标记了自动解绑
            else if (ann.autoUnBind) {
                return new AutoBindHandler(index, ann, returnMono, returnFlux,
                                           list -> getProperties(list, ann.property),
                                           (tenant, objects) -> doUnbind(ann, tenant, objects)
                );
            }
            return new AssetsHandler(index, ann, returnMono, returnFlux,
                                     obj -> getProperties((obj), ann.property));
        }

        //按id处理权限
        if (ann.assetIdIndex != -1) {
            index = ann.assetIdIndex;
            ResolvableType type = ResolvableType.forMethodParameter(method, index);
            //参数为Mono
            if (Mono.class.isAssignableFrom(type.toClass())) {
                if (ann.autoBind) { //绑定
                    return new MonoAutoBindHandler(index, ann, returnMono, returnFlux,
                                                   Optional::of,
                                                   (tenant, objects) -> tenant.bindAssets(ann.type, objects)
                    );
                } else if (ann.autoUnBind) { //解绑
                    return new MonoAutoBindHandler(index, ann, returnMono, returnFlux,
                                                   Optional::of,
                                                   (tenant, objects) -> doUnbind(ann, tenant, objects)
                    );
                }
                return new MonoAssetHandler(index, ann, Function.identity());
            }
            //参数为Flux
            if (Flux.class.isAssignableFrom(type.toClass())) {
                if (ann.autoBind) { //绑定
                    return new FluxAutoBindHandler(index, ann, returnMono, returnFlux,
                                                   list -> list,
                                                   (tenant, objects) -> tenant.bindAssets(ann.type, objects)
                    );
                } else if (ann.autoUnBind) { //解绑
                    return new FluxAutoBindHandler(index, ann, returnMono, returnFlux,
                                                   list -> list,
                                                   (tenant, objects) -> doUnbind(ann, tenant, objects)
                    );
                }
                return new FluxAssetsHandler(index, ann, Function.identity());
            }
            //非响应式参数
            if (ann.autoUnBind) {
                return new AutoBindHandler(index, ann, returnMono, returnFlux,
                                           list -> list,
                                           (tenant, objects) -> doUnbind(ann, tenant, objects)
                );
            } else if (ann.autoBind) {
                return new AutoBindHandler(index, ann, returnMono, returnFlux,
                                           list -> list,
                                           (tenant, objects) -> tenant.bindAssets(ann.type, objects)
                );
            }
            return new AssetsHandler(index, ann, returnMono, returnFlux, Function.identity());
        }

        return Handler.DO_NOTING;
    }

    private static Mono<AssetsHolder> getCurrentHolder(boolean required) {
        return required
            ? AssetsHolder
            .currentReactive()
            //没有资产信息时抛出AccessDenyException
            .switchIfEmpty(Mono.error(() -> new AccessDenyException("error.access_denied", "need_assets_holder")))
            : AssetsHolder.currentReactive();
    }

    @AllArgsConstructor
    @EqualsAndHashCode
    static class CacheKey {

        final Method method;
        final Class<?> target;

    }

    interface Handler {

        //啥也不做
        Handler DO_NOTING = new Handler() {
            @Override
            public Phased getPhased() {
                return Phased.before;
            }

            @Override
            public Object handle(Object[] args, Object result, Phased phased) {
                return result;
            }
        };

        //验证阶段
        Phased getPhased();

        //处理权限控制
        Object handle(Object[] args, Object result, Phased phased);

        //是否开启事务
        default boolean transactional() {
            return false;
        }
    }

    //从对象中获取属性
    private static Optional<Object> getProperty(Object obj, String prop) {
        //转为map后获取
        Map<String, Object> property = FastBeanCopier.copy(obj, new HashMap<>(), FastBeanCopier.include(prop));
        return Optional.ofNullable(property.get(prop));
    }

    //从多个对象中获取属性
    private static List<Object> getProperties(Collection<?> obj, String prop) {
        return obj
            .stream()
            .map(v -> getProperty(v, prop).orElse(null))
            .filter(Objects::nonNull)
            .collect(Collectors.toList());
    }

    /**
     * 组合控制器
     *
     * @see MultiAssetsController
     */
    static class CompositeHandler implements Handler {
        private final List<Handler> handlers = new ArrayList<>();
        private boolean transactional;

        public void addHandler(Handler handler) {
            handlers.add(handler);
            //任意一个handler开启事务则开启
            if (handler.transactional()) {
                this.transactional = true;
            }
        }

        @Override
        public Phased getPhased() {
            return Phased.both;
        }

        @Override
        public Object handle(Object[] args, Object result, Phased phased) {
            for (Handler handler : handlers) {
                if (handler.getPhased() == phased || handler.getPhased() == Phased.both) {
                    result = handler.handle(args, result, phased);
                }
            }
            return result;
        }

        @Override
        public boolean transactional() {
            return transactional;
        }
    }

    /**
     * 参数为Flux的资产对象处理器
     */
    static class FluxAutoBindHandler implements Handler {
        private final int argIndex;
        private final AssetsDefine ann;
        private final boolean returnMono;
        private final Function<List<?>, Collection<?>> argMapper;
        private final BiFunction<AssetsHolder, Collection<?>, Mono<?>> afterAction;
        private final BiFunction<Mono<List<?>>, Publisher<?>, Mono<Object>> afterHandler;

        public FluxAutoBindHandler(int argIndex,
                                   AssetsDefine ann,
                                   boolean returnMono,
                                   boolean returnFlux,
                                   Function<List<?>, Collection<?>> argMapper,
                                   BiFunction<AssetsHolder, Collection<?>, Mono<?>> afterAction) {
            this.argIndex = argIndex;
            this.ann = ann;
            this.returnMono = returnMono;
            this.argMapper = argMapper;
            this.afterAction = afterAction;

            //初始化方法执行完之后的处理逻辑
            this.afterHandler = (argMono, executor) -> Mono
                .zip(AssetsHolder.currentReactive(), argMono)
                .map(tp2 -> Mono
                    //executor即被aop拦截到方法的返回值,也是真实的业务逻辑.执行之
                    .from(executor)
                    .then(
                        //执行完后进行绑定或者解绑操作
                        Mono.defer(() -> afterAction.apply(tp2.getT1(), argMapper.apply(tp2.getT2())))
                    )
                    .cast(Object.class)
                )
                //没有资产持有者信息,说明可能是系统级的用户,则尝试解绑全部资产.
                //场景: 管理员删除了资产数据,应该要清空所有此资产的绑定关系.
                .defaultIfEmpty(tryUnbindAll(argMono))
                .flatMap(Function.identity());
        }


        @Override
        public Phased getPhased() {
            return Phased.both;
        }

        @Override
        @SuppressWarnings("all")
        public Object handle(Object[] args, Object result, Phased phased) {

            if (phased == Phased.before) {
                //执行之前通过修改参数来处理权限控制
                Flux<?> argCache = ((Flux<?>) args[argIndex]).cache();
                if (ann.validate) {
                    @SuppressWarnings("all")
                    Flux<?> arg = Mono
                        .zip(
                            //当前资产持有者
                            AssetsHolder.currentReactive(),
                            //将所有参数收集为list
                            argCache.collectList(),
                            (holder, assets) -> {
                                return holder
                                    //校验权限
                                    .assertPermission(ann.type, argMapper.apply(assets), ann.allowAssetNotExist, ann.permission)
                                    .<Object>thenMany(Flux.fromIterable(assets));
                            })
                        .flatMapMany(Function.identity())
                        .switchIfEmpty(argCache);

                    args[argIndex] = arg;
                } else {
                    args[argIndex] = argCache;
                }

                return result;
            }
            //执行之后对资产进行绑定
            else {
                //收集参数并缓存
                Mono<List<?>> argCache = (Mono) (
                    ((Flux<?>) args[argIndex])
                        .collectList()
                        .cache()
                );

                if (returnMono) {
                    //cache 方法的返回值 ,实际逻辑只执行一次.
                    Mono<Object> mono = ((Mono<Object>) result).cache();
                    return afterHandler.apply(argCache, mono).then(mono);
                } else {
                    Flux<Object> flux = ((Flux<Object>) result).cache();
                    return afterHandler.apply(argCache, flux).thenMany(flux);
                }
            }
        }

        public Mono<Object> tryUnbindAll(Mono<List<?>> argMono) {
            //配置了解绑并且需要解绑全部时
            if (ann.autoUnBind && ann.unbindAll) {
                return argMono.flatMap(arg -> afterAction.apply(null, arg));
            }
            return Mono.empty();
        }

        @Override
        public boolean transactional() {
            return true;
        }
    }

    /**
     * 参数为Mono的资产对象处理器
     */
    static class MonoAutoBindHandler implements Handler {
        private final int argIndex;
        private final AssetsDefine ann;
        private final boolean returnMono;
        private final Function<Object, Optional<?>> argMapper;
        private final BiFunction<AssetsHolder, Object, Mono<?>> afterAction;
        private final BiFunction<Mono<?>, Publisher<?>, Mono<Object>> afterHandler;

        public MonoAutoBindHandler(int argIndex,
                                   AssetsDefine ann,
                                   boolean returnMono,
                                   boolean returnFlux,
                                   Function<Object, Optional<?>> argMapper,
                                   BiFunction<AssetsHolder, Collection<?>, Mono<?>> afterAction) {
            this.argIndex = argIndex;
            this.ann = ann;
            this.returnMono = returnMono;
            this.argMapper = argMapper;
            //方法执行后的动作,绑定，解绑?
            this.afterAction = (member, arg) -> afterAction
                .apply(
                    member,
                    argMapper
                        .apply(arg)
                        .map(CastUtils::castArray)
                        .orElseThrow(() -> new IllegalArgumentException("can not get id property"))
                );
            //方法执行后的处理逻辑
            this.afterHandler = (argMono, stream) -> Mono
                .zip(getCurrentHolder(ann.required), argMono)
                .map(tp2 -> Mono
                    .from(stream)
                    .then(
                        //进行绑定或者解绑操作
                        Mono.defer(() -> this.afterAction.apply(tp2.getT1(), tp2.getT2()))
                    )
                    .cast(Object.class)
                )
                .defaultIfEmpty(tryUnbindAll(argMono))
                .flatMap(Function.identity());
        }

        @Override
        public Phased getPhased() {
            return Phased.both;
        }

        @Override
        @SuppressWarnings("all")
        public Object handle(Object[] args, Object result, Phased phased) {

            //在方法调用之前,修改参数,来进行权限控制.
            if (phased == Phased.before) {
                //可能多次订阅,使用cache
                Mono<?> argCache = ((Mono<?>) args[argIndex]).cache();

                if (ann.validate) {
                    //修改参数
                    args[argIndex] = Mono
                        .zip(
                            //当前资产持有者
                            getCurrentHolder(ann.required),
                            //将所有参数收集为list
                            argCache,
                            (holder, asset) -> {
                                //获取对象中的资产ID
                                Object assetId = argMapper.apply(asset).orElse(null);
                                if (assetId == null) {
                                    return Mono.just(asset);
                                }
                                //校验权限
                                return holder
                                    .assertPermission(ann.type, CastUtils.castArray(assetId), ann.allowAssetNotExist, ann.permission)
                                    .<Object>thenReturn(asset);
                            })
                        .flatMap(Function.identity())
                        //当前用户不支持任何资产权限控制(系统管理员等)
                        .switchIfEmpty(argCache);
                } else {
                    args[argIndex] = argCache;
                }
                return result;
            } else {
                Mono<?> arg = ((Mono<?>) args[argIndex]).cache();

                if (returnMono) {
                    //cache ,实际逻辑只执行一次.
                    @SuppressWarnings("all")
                    Mono<Object> mono = ((Mono<Object>) result).cache();
                    return afterHandler
                        .apply(arg, mono)
                        //返回原始结果
                        .then(mono);
                } else {
                    //cache ,实际逻辑只执行一次.
                    @SuppressWarnings("all")
                    Flux<Object> flux = ((Flux<Object>) result).cache();
                    return afterHandler
                        .apply(arg, flux)
                        //返回原始结果
                        .thenMany(flux);
                }
            }

        }

        public Mono<Object> tryUnbindAll(Mono<?> argMono) {
            //配置来自动解绑或者解绑全部则调用解绑逻辑
            if (ann.autoUnBind && ann.unbindAll) {
                return argMono.flatMap(arg -> afterAction.apply(null, arg));
            }
            return Mono.empty();
        }

        @Override
        public boolean transactional() {
            return true;
        }
    }

    /**
     * 根据参数中的ID来处理权限
     */
    @AllArgsConstructor
    static class AutoBindHandler implements Handler {
        private final int argIndex;

        private final AssetsDefine ann;
        private final boolean returnMono;
        private final boolean returnFlux;

        private final Function<Collection<?>, Collection<?>> idMapper;

        private final BiFunction<AssetsHolder, Collection<?>, Mono<Void>> afterHandler;

        @Override
        public Phased getPhased() {
            return Phased.after;
        }

        @Override
        @SuppressWarnings("all")
        public Object handle(Object[] args, Object result, Phased phased) {

            Collection<?> id = idMapper.apply(CastUtils.castArray(args[argIndex]));

            return getCurrentHolder(ann.required)
                .flatMap(holder -> ann.validate
                    //验证是否有权限
                    ? holder.assertPermission(ann.type, id, ann.allowAssetNotExist, ann.permission)
                            .thenReturn(holder)
                    : Mono.just(holder))
                //1.有资产持有信息,则使用传入的执行逻辑
                .map(holder -> afterHandler.apply(holder, id))
                //2. 如果没有资产持有信息并且设置了解绑全部,则使用解绑逻辑,否则啥也不做
                .defaultIfEmpty((ann.autoUnBind && ann.unbindAll) ? Mono.defer(() -> afterHandler.apply(null, id)) : Mono.empty())
                //组合1，2步的逻辑
                .flatMap(Function.identity())
                //转换返回结果
                .as(v -> returnMono ? v.then(((Mono<?>) result)) : v.thenMany(((Flux<?>) result)));

        }

        @Override
        public boolean transactional() {
            return true;
        }
    }

    /**
     * 参数是Mono,但是只校验权限
     */
    @AllArgsConstructor
    static class MonoAssetHandler implements Handler {
        private final int argIndex;

        private final AssetsDefine ann;

        private final Function<Object, Object> assetIdMapper;


        @Override
        public Phased getPhased() {
            return Phased.before;
        }

        @Override
        @SuppressWarnings("all")
        public Object handle(Object[] args, Object result, Phased phased) {
            Mono<?> argCache = ((Mono<?>) args[argIndex]).cache();

            args[argIndex] = Mono
                .zip(getCurrentHolder(ann.required),
                     argCache,
                     (holder, arg) -> {
                         Object assetId = assetIdMapper.apply(arg);
                         if(assetId instanceof Optional){
                             assetId = ((Optional<?>) assetId).get();
                         }
                         //没有资产ID?
                         if (assetId == null) {
                             log.warn("can not get assetId [type={},property={}] from {}", ann.type, ann.property, arg);
                             return Mono.just(arg);
                         }
                         //校验权限
                         return holder
                             .assertPermission(ann.type, CastUtils.castArray(assetId), ann.allowAssetNotExist, ann.permission)
                             .thenReturn(arg);
                     })
                //上游返回的Mono<Mono>,使用此方式进行组合,否则逻辑不会执行
                .<Object>flatMap(Function.identity())
                .switchIfEmpty(argCache);

            return null;
        }
    }

    /**
     * 参数是Flux,但是只校验权限
     */
    @AllArgsConstructor
    static class FluxAssetsHandler implements Handler {
        private final int argIndex;

        private final AssetsDefine ann;

        private final Function<Collection<?>, Collection<?>> assetIdMapper;

        @Override
        public Phased getPhased() {
            return Phased.before;
        }

        @Override
        public Object handle(Object[] args, Object result, Phased phased) {

            Flux<?> argCache = ((Flux<?>) args[argIndex]).cache();
            //修改参数
            args[argIndex] = Mono
                .zip(getCurrentHolder(ann.required),
                     argCache.collectList(),
                     (holder, assets) -> {
                         Collection<?> assetIds = assetIdMapper.apply(assets);
                         //没有资产ID?
                         if (CollectionUtils.isEmpty(assetIds)) {
                             log.warn("can not get assetId [type={},property={}]", ann.type, ann.property);
                             return Flux.fromIterable(assets);
                         }
                         //校验权限
                         return holder
                             .assertPermission(ann.type, assetIds, ann.allowAssetNotExist, ann.permission)
                             .thenMany(Flux.fromIterable(assets));
                     })
                //上游返回的Mono<Flux>,使用此方式进行组合,否则逻辑不会执行
                .<Object>flatMapMany(Function.identity())
                .switchIfEmpty(argCache);

            return result;
        }
    }

    /**
     * 参数是不为Mono或者Flux
     */
    @AllArgsConstructor
    static class AssetsHandler implements Handler {
        private final int argIndex;

        private final AssetsDefine ann;

        private final boolean returnMono;
        private final boolean returnFlux;

        private final Function<Collection<?>, Collection<?>> idMapper;

        @Override
        public Phased getPhased() {
            return Phased.after;
        }

        @Override
        @SuppressWarnings("all")
        public Object handle(Object[] args, Object result, Phased phased) {
            //获取参数中的资产ID
            Collection<?> id = idMapper.apply(CastUtils.castArray(args[argIndex]));

            //构造新的返回值,在返回之前进行权限校验
            Mono<?> validator = getCurrentHolder(ann.required)
                .flatMap(holder -> holder.assertPermission(ann.type, id, ann.allowAssetNotExist, ann.permission));

            if (returnMono) {
                return validator.then(((Mono<?>) result));
            }
            if (returnFlux) {
                return validator.thenMany(((Flux<?>) result));
            }
            return result;
        }
    }

    /**
     * 参数是Mono&lt;QueryParamEntity&gt;
     *
     * @see QueryParamEntity
     */
    @AllArgsConstructor
    static class MonoQueryParamHandler implements Handler {

        private final int argIndex;

        private final AssetsDefine ann;

        @Override
        public Phased getPhased() {
            return Phased.before;
        }

        @Override
        @SuppressWarnings("all")
        public Object handle(Object[] args, Object result, Phased phased) {

            Mono<QueryParamEntity> argCache = ((Mono<QueryParamEntity>) args[argIndex]).cache();

            args[argIndex] = Mono
                .zip(
                    //资产持有者
                    getCurrentHolder(ann.required),
                    argCache,
                    (holder, param) -> {
                        //注入条件
                        return holder.injectQuery(param, ann.type, ann.property);
                    }
                )
                .switchIfEmpty(argCache);
            return result;
        }
    }

    /**
     * 参数是QueryParamEntity时.
     * 这种情况有一个缺陷:
     * <p>
     * 在方法被调用时,参数其实没有发生任何变化,只有当返回值(Mono或者Flux)被订阅时,参数才会进行注入.
     * <p>
     * 所以方法的逻辑应该进行延迟处理,比如:
     * <pre>{@code
     *
     *  public Flux<MyEntity> doQuery(QueryParamEntity param){
     *      return Flux.defer(()->service.doQuery(param))
     *  }
     *
     * }</pre>
     *
     * @see QueryParamEntity
     */
    @AllArgsConstructor
    static class QueryParamHandler implements Handler {

        private final int argIndex;

        private final AssetsDefine ann;

        private final boolean returnMono;
        private final boolean returnFlux;

        @Override
        public Phased getPhased() {
            return Phased.after;
        }

        @Override
        @SuppressWarnings("all")
        public Object handle(Object[] args, Object result, Phased phased) {

            QueryParamEntity param = (QueryParamEntity) args[argIndex];

            //在方法逻辑执行前注入条件
            Mono<?> mono = getCurrentHolder(ann.required)
                .doOnNext(holder -> holder.injectQuery(param, ann.type, ann.property));

            if (returnMono) {
                return mono.then(((Mono<?>) result));
            } else if (returnFlux) {
                return mono.thenMany(((Flux<?>) result));
            } else {
                return result;
            }
        }
    }


}
